package com.study.mike.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.alibaba.excel.EasyExcel;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.study.mike.common.constants.Constant;
import com.study.mike.common.exception.BizException;
import com.study.mike.mapper.ContractMapper;
import com.study.mike.model.dto.ContractDTO;
import com.study.mike.model.dto.PageDTO;
import com.study.mike.model.entity.ContractDO;
import com.study.mike.model.po.DeleteBatchPO;
import com.study.mike.model.po.SaveOrUpdateContractPO;
import com.study.mike.model.query.ContractQuery;
import com.study.mike.service.ContractService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @author KangHui
 * @Date 2023/9/20 15:55
 */
@Service
@Slf4j
public class ContractServiceImpl extends ServiceImpl<ContractMapper, ContractDO> implements ContractService {
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean saveOrUpdate(SaveOrUpdateContractPO po) {
        ContractDO contractDO = new ContractDO();
        BeanUtils.copyProperties(po, contractDO);
        contractDO.setContractTag(CollUtil.isEmpty(po.getContractTag()) ? null : JSONUtil.toJsonStr(po.getContractTag()));
        contractDO.setServerScope(CollUtil.isEmpty(po.getServerScope()) ? null : JSONUtil.toJsonStr(po.getServerScope()));
        contractDO.setDistrict(CollUtil.isEmpty(po.getDistrict()) ? null : JSONUtil.toJsonStr(po.getDistrict()));
        contractDO.setDistrictId(CollUtil.isEmpty(po.getDistrictId()) ? null : JSONUtil.toJsonStr(po.getDistrictId()));
        // 计算时长
        if (ObjectUtil.isAllNotEmpty(po.getSignTime(), po.getExpireTime())) {
            long between = DateUtil.between(po.getSignTime(), po.getExpireTime(), DateUnit.DAY);
            contractDO.setContractDuration(between / 30);
        }
        try {
            return this.saveOrUpdate(contractDO);
        } catch (DuplicateKeyException e) {
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            throw new BizException("400", "流程编号已存在");
        }
    }

    @Override
    public PageDTO<ContractDTO> queryContract(ContractQuery po) {
        LambdaQueryWrapper<ContractDO> wrapper = this.queryWrapper(po);
        PageDTO<ContractDO> page = new PageDTO<>(po.getPageCurrent(), po.getPageSize());
        IPage<ContractDO> pages = this.page(page, wrapper);
        List<ContractDO> contractDOS = pages.getRecords();
        List<ContractDTO> records = contractDOS.stream().map(x -> {
            ContractDTO contractDTO = this.contractDTOInfo(x);
            return contractDTO;
        }).collect(Collectors.toList());
        PageDTO<ContractDTO> result = new PageDTO<>(pages.getCurrent(), pages.getSize(), pages.getTotal());
        result.setRecords(records);
        page.setRecords(records);
        return result;
    }

    @Override
    public ContractDTO contractInfo(ContractQuery po) {
        LambdaQueryWrapper<ContractDO> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(ContractDO::getId, po.getId());
        ContractDO contractDO = this.getOne(wrapper);
        return this.contractDTOInfo(contractDO);
    }

    @Override
    public Boolean deleteBatch(DeleteBatchPO po) {
        //待删除数据列表
        List<Long> ids = new ArrayList<>();
        // 部分数据无法正常删除
        for (Long id : po.getIds()) {
            ContractDO contractDO = this.getById(id);
            if (ObjectUtil.equal(0, contractDO.getContractSpecial())) {
                ids.add(id);
            } else {
                log.error("当前数据无法删除{【】}" + contractDO.getFlowNo());
            }
        }
        return this.removeByIds(ids);
    }

    @Override
    public String export(ContractQuery po) {
        if (!Arrays.asList(1, 2, 3).contains(po.getExportType())) {
            throw new BizException("400", "当前导出方式无效");
        }
        if (ObjectUtil.equal(po.getExportType(), Constant.ExportType.SELECT) && CollUtil.isEmpty(po.getIds())) {
            throw new BizException("400", "请选择导出数据");
        }
        // 导入文件保存地址
        String filePath = "C:\\Users\\James\\Desktop\\" + DateUtil.format(new Date(), DatePattern.PURE_DATETIME_MS_PATTERN) + ".xlsx";
        // 获取到导出的数据
        List<ContractDTO> contractDTOS;
        if (ObjectUtil.equal(po.getExportType(), Constant.ExportType.CURRENT)) {
            contractDTOS = this.queryContract(po).getRecords();
        } else if (ObjectUtil.equal(po.getExportType(), Constant.ExportType.SELECT)) {
            contractDTOS = this.queryContractInfo(po);
        } else {
            contractDTOS = this.queryContractInfo(po);
        }
        EasyExcel.write(filePath, ContractDTO.class).sheet("Sheet1").doWrite(contractDTOS);
        // 文件上传获取下载地址
        return "success";
    }

    @Override
    public Boolean importContract() {
        return null;
    }

    private List<ContractDTO> queryContractInfo(ContractQuery po) {
        LambdaQueryWrapper<ContractDO> wrapper = new LambdaQueryWrapper<>();
        wrapper.in(CollUtil.isNotEmpty(po.getIds()), ContractDO::getId, po.getIds())
                .eq(ContractDO::getCompanyCode, po.getCompanyCode());
        List<ContractDO> list = this.list(wrapper);
        return JSONUtil.toList(JSONUtil.toJsonStr(list), ContractDTO.class);
    }

    private ContractDTO contractDTOInfo(ContractDO contractDO) {
        ContractDTO contractDTO = new ContractDTO();
        BeanUtils.copyProperties(contractDO, contractDTO);
        ContractDTO cover = this.contractCover(contractDO);
        contractDTO.setServerScope(cover.getServerScope());
        contractDTO.setContractTag(cover.getContractTag());
        contractDTO.setDistrict(cover.getDistrict());
        contractDTO.setDistrictId(cover.getDistrictId());
        return contractDTO;
    }

    private LambdaQueryWrapper<ContractDO> queryWrapper(ContractQuery po) {
        LambdaQueryWrapper<ContractDO> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(StrUtil.isNotBlank(po.getFlowNo()), ContractDO::getFlowNo, po.getFlowNo())
                .eq(StrUtil.isNotBlank(po.getContractNo()), ContractDO::getContractNo, po.getContractNo())
                .eq(StrUtil.isNotBlank(po.getLocationNo()), ContractDO::getLocationNo, po.getLocationNo())
                .like(StrUtil.isNotBlank(po.getContractName()), ContractDO::getContractName, po.getContractName())
                .eq(ObjectUtil.isNotNull(po.getContractType()), ContractDO::getContractType, po.getContractType())
                .eq(ObjectUtil.isNotNull(po.getCooperationMode()), ContractDO::getCooperationMode, po.getCooperationMode())
                .like(StrUtil.isNotBlank(po.getProjectLeader()), ContractDO::getProjectLeader, po.getProjectLeader())
                .between(ObjectUtil.isAllNotEmpty(po.getStartSignTime(), po.getEndSignTime()), ContractDO::getSignTime, po.getStartSignTime(), po.getEndSignTime())
                .between(ObjectUtil.isAllNotEmpty(po.getStartExpireTime(), po.getEndExpireTime()), ContractDO::getExpireTime, po.getStartExpireTime(), po.getEndExpireTime())
                .eq(ObjectUtil.isNotNull(po.getAmountType()), ContractDO::getAmountType, po.getAmountType())
                .like(StrUtil.isNotBlank(po.getContractObject()), ContractDO::getContractObject, po.getContractObject())
                .like(StrUtil.isNotBlank(po.getFirstPartyName()), ContractDO::getFirstPartyName, po.getFirstPartyName())
                .eq(ObjectUtil.isNotNull(po.getFirstPartyType()), ContractDO::getFirstPartyType, po.getFirstPartyType())
                .eq(ObjectUtil.isNotNull(po.getTradeType()), ContractDO::getTradeType, po.getTradeType())
                .like(StrUtil.isNotBlank(po.getContacts()), ContractDO::getContacts, po.getContacts())
                .like(StrUtil.isNotBlank(po.getContactsMobile()), ContractDO::getContactsMobile, po.getContactsMobile())
                .like(StrUtil.isNotBlank(po.getDetailAddress()), ContractDO::getDetailedAddress, po.getDetailAddress())
                .between(ObjectUtil.isAllNotEmpty(po.getStartExactAmount(), po.getEndExactAmount()), ContractDO::getExactAmount, po.getStartExactAmount(), po.getEndExactAmount())
                .between(ObjectUtil.isAllNotEmpty(po.getStartContractDuration(), po.getEndContractDuration()), ContractDO::getContractDuration,
                        po.getStartContractDuration(), po.getEndContractDuration())
                .eq(StrUtil.isNotBlank(po.getCompanyCode()), ContractDO::getCompanyCode, po.getCompanyCode())
                .orderByDesc(ContractDO::getId);
        List<String> serviceScope = po.getServerScope();
        if (CollUtil.isNotEmpty(serviceScope)) {
            serviceScope.forEach(x -> {
                wrapper.like(StrUtil.isNotBlank(x), ContractDO::getServerScope, x).or();
            });

        }
        return wrapper;
    }


    private ContractDTO contractCover(ContractDO contractDO) {
        // serverScope、contractTag、district、districtId
        String serverScope = contractDO.getServerScope();
        String contractTag = contractDO.getContractTag();
        String district = contractDO.getDistrict();
        String districtId = contractDO.getDistrictId();
        ContractDTO contractDTO = new ContractDTO();
        contractDTO.setServerScope(StrUtil.isNotBlank(serverScope) ? JSONUtil.toList(serverScope, String.class) : null);
        contractDTO.setContractTag(StrUtil.isNotBlank(contractTag) ? JSONUtil.toList(contractTag, String.class) : null);
        contractDTO.setDistrict(StrUtil.isNotBlank(district) ? JSONUtil.toList(district, String.class) : null);
        contractDTO.setDistrictId(StrUtil.isNotBlank(districtId) ? JSONUtil.toList(districtId, Long.class) : null);
        return contractDTO;
    }
}
